# -*-coding: UTF-8 -*-
import Pyro.core
import thread
import threading
import time
import re
import sys
import traceback
import os
from ConstantesFrogger import *
from PlancheDeJeu.PlancheDeJeu import *
from PlancheDeJeu.Joueur import *
from MiniJeux.PartieMiniJeu import *
import MiniJeux.FrogShooter.FrogShooter as FrogShooter
import MiniJeux.FrogDance.PlancherDeDance as FrogDance
import MiniJeux.FrogLab.FrogLabModel as FrogLab
import MiniJeux.FrogMouches.JeuDeMouches as FrogMouches

class PartieStartedError(Exception):
    def __init__(self, str):
        Exception.__init__(self, str)

class InvalidNameError(Exception):
    def __init__(self, str):
        Exception.__init__(self, str)

class DuplicateNameError(Exception):
    def __init__(self, str):
        Exception.__init__(self, str)

class Partie():
    id = 0
    
    def __init__(self,nbClients, chat):
        print("Partie::__init__()")
        self.reseau = False
        self.chat = chat
        self.dictJoueurs = {}
        self.dictMiniJeux = {}
        self.dictPlancheDeJeux = {}
        self.nbMaxNiveaux = 3
        self.miniGameTime = False
        self.partieGagnee = False
        self.nbClients = nbClients
        self.started = False
        self.miniJeuEnCours = None
        self.miniGameChosenOne = None
        self.syncReussis = False

        #1 Creer tous les mini jeux localement
        #FrogShooter
        nbCible = 10
        temps = 15
        frogShooter = FrogShooter.Modele((600,350),nbCible,temps)
        self.dictMiniJeux[frogShooter.nom] = frogShooter
        #FrogDance
        frogDance = FrogDance.PlancherDeDance()
        self.dictMiniJeux[frogDance.nom] = frogDance
        #FrogLab
        frogLab = FrogLab.ModelFrogLab([])
        self.dictMiniJeux[frogLab.nom] = frogLab
        #FrogLab
        frogMouches = FrogMouches.JeuDeMouches(54,36, [])
        self.dictMiniJeux[frogMouches.nom] = frogMouches

    def getDictJoueurs(self):
        return self.dictJoueurs.values()

    def getListeJoueurs(self):
        """ Deprecated. """
        return self.getDictJoueurs()

    def creerPlancheDeJeu(self):
        for i in self.dictJoueurs.keys():
            self.dictPlancheDeJeux[i]=(PlancheDeJeu(self,self.dictJoueurs[i],int(i)+1))

    # isGameOver()
    # Retourne vrai si la partie est terminée.
    def isGameOver(self):
        return self.partieGagnee
    
    # creerJoueur() 
    # Creer l'objet Joueur et retourne un identifiant utilisé
    # retourne 'PARTIE_STARTED' si la partie est commencée.
    # retourne -1 si le nom est deja pris.
    # pour appeler les autre méthodes de Partie. 
    def creerJoueur(self,nom):
        if self.started:
            raise PartieStartedError('La partie est déjà commencée!')
        nom = nom.strip()
        rex = re.compile('^[a-zA-Z0-9 _-]*$')
        if nom == '' or len(nom)>15 or not rex.match(nom):
            raise InvalidNameError('Ce nom est invalide!')
        for j in self.dictJoueurs.values():
            if j and j.nom == nom:
                raise DuplicateNameError('Ce nom existe déjà dans cette Partie!')
        id = Partie.id
        Partie.id += 1
        joueur = Joueur(nom, Position(7,0), id)
        self.dictJoueurs[id] = joueur
        self.dictPlancheDeJeux[id] = PlancheDeJeu(self,joueur)
        if len(self.dictJoueurs) >= self.nbClients:
            print "5-------------ATTEINT"
            self.started = True
        return id
    
    # getJoueurById()
    # Retourne un objet Joueur par le Id retourné par creerJoueur().
    def getJoueurById(self,joueurId):
        return self.dictJoueurs[joueurId]
    
    # nbMiniJeuxDisponibles()
    # Retourne le nombre de mini-jeux disponibles.
    def nbMiniJeuxDisponibles(self):
        return len(self.dictMiniJeux)
    
    def pauseGame(self):
        for i in self.dictJoueurs.keys():
            self.dictPlancheDeJeux[i].pause()
            self.dictJoueurs[i].resetDirections()
            
    
    # launchMiniJeu()
    # Démarre un mini-jeu.
    # Indice doit étre entre 0 et nbMiniJeuxDisponibles()-1.
    def launchMiniJeu(self, id):
        self.dictJoueurs[id].checkIn()
        self.miniGameTime = True
        for i in self.dictJoueurs.keys():
            self.dictPlancheDeJeux[i].pause()
        self.sync(id, self.playMiniGame)
    
    def setMiniGame(self, nomJeu):
        self.miniJeuEnCours = self.dictMiniJeux[nomJeu]
        self.miniJeuEnCours.termine = False
    
    def getMiniGameName(self, id):
        self.dictJoueurs[id].checkIn()
        if self.miniJeuEnCours:
            return self.miniJeuEnCours.nom
        else:
            return None
    
    def playMiniGame(self):
        self.miniJeuEnCours.start(self.getListeJoueurs())
        self.chat.ajouterMessage(self.miniJeuEnCours.description, True)
        self.miniJeuEnCours.play()
    
    def isTheChosenOne(self, id):
        return self.miniGameChosenOne == id

    def getChosenOneName(self):
        return self.dictJoueurs[self.miniGameChosenOne].nom
    
    def setMiniGameTime(self, pdj, b):
        self.miniJeuEnCours = None
        for i, j in self.dictPlancheDeJeux.items():
            if j == pdj:
                self.miniGameChosenOne = i
        self.miniGameTime = b

    def isMiniJeuTermine(self, id):
        self.dictJoueurs[id].checkIn()
        return self.miniJeuEnCours.termine
        
    def isMiniGameTime(self, id):
        self.dictJoueurs[id].checkIn()
        return self.miniGameTime
    
    # launchPlancheDeJeu()
    # Démarre un planche de jeu pour un certain joueur.
    def launchPlancheDeJeu(self,joueurId):
        self.dictJoueurs[joueurId].checkIn()      
        self.sync(joueurId, self.resumePlancheDeJeu)
        self.miniGameTime = False

    def getMiniJeuEnCours(self):
        return self.miniJeuEnCours
        
    def resumePlancheDeJeu(self):
        #Ajouter les scores au joueurs
        self.miniGameTime = False
        
        if self.miniJeuEnCours:
            ls = self.miniJeuEnCours.getPointsFinal()
            for joueur in self.dictJoueurs.values():
                self.chat.ajouterMessage(joueur.nom+ " a gagné "+str(ConstantesFrogger.MINIJEU_POINTS[ls[joueur.nom]])+"pts")
                joueur.score += ConstantesFrogger.MINIJEU_POINTS[ls[joueur.nom]]

        for pdj in self.dictPlancheDeJeux.values():
            pdj.isFrozen = True
            pdj.verifierAnses()

        for jo in self.dictJoueurs.values():
            if jo.niveau > self.nbMaxNiveaux:
                self.partieGagnee = True
                gagnant = None
                for j in self.dictJoueurs.values():
                    if not gagnant:
                        gagnant = j
                    elif gagnant.score < j.score:
                        gagnant = j
                self.chat.ajouterMessage(gagnant.nom+" a gagné la partie!")
                print "VOUS AVEZ GAGNER AGILEMENT"
                return;

        for pdj in self.dictPlancheDeJeux.values():
            pdj.play()

        self.miniJeuEnCours = None

    def getPlancheDeJeu(self,joueurId):
        return self.dictPlancheDeJeux[joueurId]
    
    def getListeMiniJeux(self):
        listeMJ = list(self.dictMiniJeux.keys())
        return listeMJ

    def quitter(self):
        pass
    
    def isStarted(self, id):
        self.dictJoueurs[id].checkIn()
        return self.started

    def getNbJoueurs(self):
        return len(self.dictJoueurs)

    def getNbClients(self):
        return self.nbClients
    
    def sync(self, id, fonct = None):
        #pass
        print "sync local"
        if fonct:
            fonct()
    
    def cleanUpJoueurs(self):
        pass
    
    def quitJoueur(self, id):
        pass

    def isReseau(self):
        return self.reseau

class PartieReseau(Partie,Pyro.core.ObjBase):
    def __init__(self,serveur,nbClients, chat):
        Partie.__init__(self,nbClients, chat)
        Pyro.core.ObjBase.__init__(self)
        self.reseau = True
        self.nbClientsConnectes = 0
        self.event = threading.Event()
        self.serveur = serveur
        self.dictPlancheDeJeuxURI = {}
        self.dictMiniJeuxURI = {}
        self.nbJoueursDebutSync = -1
        self.lock = thread.allocate_lock()
        
        self.jobCleanUpJoueurs = Job(2, self.cleanUpJoueurs)
        self.jobCleanUpJoueurs.start()
        
        # Creer toutes les planches de jeu sur le serveur (nbMaxNiveaux)
        #for i in range(len(self.listePlancheDeJeux)):
        #    self.listePlancheDeJeuxURI.append(serveur.creerPlancheDeJeu(self.listePlancheDeJeux[i],self.listeJoueurs[i].nom))

        # Mettre tous les mini jeux sur le serveur avec le URI
        #self.listeMiniJeuxURI.append(serveur.creerMiniJeuReseau(self.listeMiniJeux[0],'PartieRSReseau'))
        #2 pas vraimenr necessaire de garder le URI je pense.
        serveur.creerMiniJeuReseau(self.dictMiniJeux['FrogShooter'],'PartieFrogShooter')
        serveur.creerMiniJeuReseau(self.dictMiniJeux['FrogDance'],'PartieFrogDance')
        serveur.creerMiniJeuReseau(self.dictMiniJeux['FrogLab'],'PartieFrogLab')
        serveur.creerMiniJeuReseau(self.dictMiniJeux['FrogMouches'],'PartieFrogMouches')

    # creerJoueur() 
    # Creer l'objet Joueur et retourne un identifiant utilisé
    # pour appeler les autre méthodes de Partie. 
    def creerJoueur(self,nom):
        print "creerJoueur start"
        last = Partie.creerJoueur(self, nom)
        self.dictPlancheDeJeuxURI[last] = (self.serveur.creerPlancheDeJeu(self.dictPlancheDeJeux[last],nom))
        self.dictJoueurs[last].checkIn()
        return last

    def getPlancheDeJeu(self,joueurId):
        return self.dictPlancheDeJeuxURI[joueurId]

    def getMiniJeuEnCours(self):
        #envoyer le URI du mini jeu en cours (ou a la fin de launch mini jeu)
        return None

    def sync(self, id, fonct = None):
        print "sync: start"
        self.dictJoueurs[id].checkIn()

        self.lock.acquire()
        print "sync: lock acquired"
        if self.nbClientsConnectes == 0:
            self.syncReussis = False
            self.nbJoueursDebutSync = len(self.dictJoueurs)
            self.t = threading.Timer(2.0, lambda  : self.eventTimeOut())
            self.t.start()
        self.nbClientsConnectes += 1
        self.lock.release()
        print "sync: lock released"

        if self.nbClientsConnectes == self.nbJoueursDebutSync:
            self.t.cancel()
            print "sync: nb clients voulus atteint"
            self.syncReussis = True
            if fonct:
                fonct()
            self.event.set()
            self.event.clear()
            self.nbClientsConnectes = 0
        else:
            print "sync: waiting"
            self.event.wait()
        
        if not self.syncReussis:
            self.dictJoueurs[id].checkIn()
            self.cleanUpJoueurs()
            self.sync(id,fonct)

        print("sync: OK")
        
    def eventTimeOut(self):
        self.syncReussis = False
        self.event.set()
        self.event.clear()
        self.nbClientsConnectes = 0
        
    def cleanUpJoueurs(self):
        for id,joueur in self.dictJoueurs.items():
            if(joueur.lastCheckIn + 5 < time.time()):
                self.quitJoueur(id)
    
    def quitJoueur(self, id):
        self.dictJoueurs.pop(id)
        self.dictPlancheDeJeuxURI.pop(id)
        self.dictPlancheDeJeux.pop(id)
        if self.miniGameChosenOne != None:
            print id, self.miniGameChosenOne
            if id == self.miniGameChosenOne:
                self.setMiniGame("FrogLab")
                self.miniGameChosenOne = None
        print len(self.dictJoueurs)
        if len(self.dictJoueurs)==0 and self.started:
            t = threading.Timer(5.0, lambda  : os._exit(0))
            t.start()

"""
if __name__ == '__main__':
    print "Test Partie.py"
    partie = Partie()
    j1 = partie.creerJoueur("manuel")
    partie.launchPlancheDeJeu(j1)
"""